
#include <BRender>
#include <BMaterial>
#include <BCone>
#include "member_BCone.h"
#include "member_BShape.h"

using namespace BWE;

#define member					(*(member_BCone*)_ptr)
#define member_allocate()		_ptr = new member_BCone(this)
#define member_release()		delete (member_BCone*)_ptr

BCone::BCone()
{
	member_allocate();
}
BCone::BCone(const BString& name) : BGeometry(name)
{
	member_allocate();
}
BCone::BCone(BReal radius, BReal height, int sides, int segments, int radiusSegments)
{
	member_allocate();
	member.radius = radius;
	member.height = height;
	member.sides = sides;
	member.segments = segments;
	member.radiusSegments = radiusSegments;
	member.prepare();
	this->dirty();
}
BCone::~BCone()
{
	member_release();
}

void BCone::copy(const BShape* other)
{
	BShape::copy(other);
	if (const BCone* cone = dynamic_cast<const BCone*>(other))
	{
		member.radius = cone_member(cone)->radius;
		member.height = cone_member(cone)->height;
		member.sides = cone_member(cone)->sides;
		member.segments = cone_member(cone)->segments;
		member.radiusSegments = cone_member(cone)->radiusSegments;
		member.coses = cone_member(cone)->coses;
		member.sines = cone_member(cone)->sines;
	}
}
BShape* BCone::clone() const
{
	BCone* cone = new BCone();
	cone->copy(this);
	return cone;
}

void BCone::setRadius(BReal radius)
{
	if (member.radius != radius)
	{
		member.radius = radius;
		this->dirty();
	}
}
BReal BCone::radius() const
{
	return member.radius;
}

void BCone::setHeight(BReal height)
{
	if (member.height != height)
	{
		member.height = height;
		this->dirty();
	}
}
BReal BCone::height() const
{
	return member.height;
}

void BCone::setSides(int sides)
{
	if (member.sides != sides)
	{
		member.sides = sides;
		member.prepare();
		this->dirty();
	}
}
int BCone::sides() const
{
	return member.sides;
}

void BCone::setSegments(int segments)
{
	if (member.segments != segments)
	{
		member.segments = segments;
		this->dirty();
	}
}
int BCone::segments() const
{
	return member.segments;
}

void BCone::setBottomSegments(int radiusSegments)
{
	if (member.radiusSegments != radiusSegments)
	{
		member.radiusSegments = radiusSegments;
		this->dirty();
	}
}
int BCone::radiusSegments() const
{
	return member.radiusSegments;
}

BVector BCone::vertex(const BVector& direct) const
{
	BReal radius = member.radius;
	BReal height = member.height;
	BReal down = height / 3;
	BReal sinAngle = (radius / sqrt(radius * radius + height * height));
	BVector vertex;
	if (direct.z() > sinAngle)
	{
		vertex.z() = height - down;
	}
	else
	{
		BReal s = sqrt(direct.x() * direct.x() + direct.y() * direct.y());
		if (s > REAL_EPSILON)
		{
			BReal d = radius / s;
			vertex.x() = direct.x() * d;
			vertex.y() = direct.y() * d;
			vertex.z() = -down;
		}
	}
	return vertex;
}
BVector BCone::inertia(BReal mass) const
{
	BReal margin = this->margin();

	BReal lx = 2 * (member.radius + margin);
	BReal ly = 2 * (member.radius + margin);
	BReal lz = member.height + margin * 2;

	const BReal x2 = lx * lx;
	const BReal y2 = ly * ly;
	const BReal z2 = lz * lz;

	const BReal scale = BReal(1) / 12;

	BReal inex = mass * (ly * ly + lz * lz) * scale;
	BReal iney = mass * (lx * lx + lz * lz) * scale;
	BReal inez = mass * (lx * lx + ly * ly) * scale;

	return BVector(inex, iney, inez);
}

void BCone::refresh(BBox& box)
{
	member.rebuild();
	BReal down = member.height * BReal(0.25);
	BSpace space;
	space.min().set(-member.radius, -member.radius, -down);
	space.max().set(member.radius, member.radius, member.height - down);
	box = space;
}
void BCone::render(BRender& render)
{
	BGeometry::render(render);
}
